#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#include <vector>
#include <set>
#include <string.h>
using namespace std;
#define M '\002'
#define N '\001'
#define X '\004'
#define O ' '
#define maxr 32
#define maxc 64
#define min_sub 10

struct Point {
public:
	
	Point(int xx, int yy) : x(xx), y(yy) { }
	
	int x;
	int y;
};

struct Way {
public:
	
	Way(Point aa, Point bb) : a(aa), b(bb) { }
	
	Point a;
	Point b;
};

class AbstractMapManager {
	
public:
	
	char map[maxr][maxc];
	int now_x, now_y;
	int fin_x, fin_y;
	int difficult;
	
	AbstractMapManager() {
		now_x = 1;
		now_y = 1;
		fin_x = maxr-2;
		fin_y = maxc-2;
		difficult = 1;
	}
	
	virtual void repaint() {
		
		system("cls");
		for (int i=0; i<maxr; ++i) {
			for (int j=0; j<maxc; ++j) {
				if (i == now_x && j == now_y)
					printf("%c", M);
				else if (i == fin_x && j == fin_y)
					printf("%c", N);
				else printf("%c",map[i][j]);
			}
			printf("\n");
		}
	}
	
	virtual int score_max() {
		return 10;
	}
	
	virtual void initialization() {
		
		for (int i=0; i<maxr; ++i) {
			map[i][0] = X;
			map[i][maxc-1] = X;
		}	
		for (int i=0; i<maxc; ++i) {
			map[0][i] = X;
			map[maxr-1][i] = X;
		}
		for (int i=1; i<maxr-1; ++i) {
			for (int j=1; j<maxc-1; ++j) {
				map[i][j] = X;
			}
		}
		
		map[now_x][now_y] = O;
		map[fin_x][fin_y] = O;
		
	}
	
	virtual bool inside(int x, int y) {
		return map[x][y] == O || map[x][y] == N;
	}
	
	virtual bool fin(int x, int y) {
		return x == fin_x && y == fin_y;
	}

	virtual bool move(int difx, int dify) {
		
		int x = now_x + difx, y = now_y + dify;
		if (inside(x, y)) {
			now_x = x;
			now_y = y;
		}
		repaint();
		return fin(x, y);
	}
	
	virtual bool outside(int x, int y) {
		return x == 0 || x == maxr-1 || y == 0 || y == maxc-1;
	}
	
	virtual Point spawnminer() {
		int pt1 = rand()%2+1, pt2 = rand()%2+1;
		int x = (rand() % (maxr-3) + 1)/pt1;
		int y = (rand() % (maxc-3) + 1)/pt2;
		return Point(x,y);
	}
	
	virtual void minermove(int x, int y, bool vin) {
		
		///int saving = rand() % 2;
		//if (vin) saving = 0;
		//if (saving != 0)
			map[x][y] = O;
		if(vin) printf("test");
			
		if (x <= maxr/2 && y <= maxc/2) {
			int mining = rand() % 4;
			switch (mining) {
				case 0:
					if ((map[x][y+1] == X || vin) && !outside(x, y+1)) {
						makeminer(x, y+1, vin);
						break;
					}
				case 2:
					if ((map[x+1][y] == X || vin) && !outside(x+1, y)) {
						makeminer(x+1, y, vin);
						break;
					}
				case 1:
					if ((map[x][y-1] == X || vin) && !outside(x, y-1)) {
						makeminer(x, y-1, vin);
						break;
					}
				case 3:
					if ((map[x-1][y] == X || vin) && !outside(x-1, y))
						makeminer(x-1, y, vin);
			}
		}
		else if (x <= maxr/2 && y >= maxc/2) {
			int mining = rand() % 10;
			switch (mining) {
				case 0: case 4:
					if ((map[x][y+1] == X || vin) && !outside(x, y+1)) {
						makeminer(x, y+1, vin);
						break;
					}
				case 8: case 5: case 6:
					if ((map[x+1][y] == X || vin) && !outside(x+1, y)) {
						makeminer(x+1, y, vin);
						break;
					}
				case 1: case 2: case 9:
					if ((map[x][y-1] == X || vin) && !outside(x, y-1)) {
						makeminer(x, y-1, vin);
						break;
					}
				case 3: case 7:
					if ((map[x-1][y] == X || vin) && !outside(x-1, y))
						makeminer(x-1, y, vin);
			}
		}
		else if (x >= maxr/2 && y <= maxc/2) {
			int mining = rand() % 10;
			switch (mining) {
				
				case 0: case 4: case 9:
					if ((map[x][y+1] == X || vin) && !outside(x, y+1)) {
						makeminer(x, y+1, vin);
						break;
					}
				case 8: case 5:
					if ((map[x+1][y] == X || vin) && !outside(x+1, y)) {
						makeminer(x+1, y, vin);
						break;
					}
				case 1: case 2:
					if ((map[x][y-1] == X || vin) && !outside(x, y-1)) {
						makeminer(x, y-1, vin);
						break;
					}
				case 3: case 7: case 6:
					if ((map[x-1][y] == X || vin) && !outside(x-1, y))
						makeminer(x-1, y, vin);
			}
		}
		else {
			int mining = rand() % 4;
			switch (mining) {
				case 0:
					if ((map[x][y+1] == X || vin) && !outside(x, y+1)) {
						makeminer(x, y+1, vin);
						break;
					}
				case 2:
					if ((map[x+1][y] == X || vin) && !outside(x+1, y)) {
						makeminer(x+1, y, vin);
						break;
					}
				case 1:
					if ((map[x][y-1] == X || vin) && !outside(x, y-1)) {
						makeminer(x, y-1, vin);
						break;
					}
				case 3:
					if ((map[x-1][y] == X || vin) && !outside(x-1, y))
						makeminer(x-1, y, vin);
			}
		}
	}
	
	virtual void makeminer(int x, int y, bool vin) {
		if (x == fin_x && y == fin_y) {
			return;
		} else if (outside(x, y)){
			return;
		} else {
			//minermove(x, y, vin);
			int mining = rand() % 3;
			int saving = rand() % 2;
			if (vin) saving = 1;
			if (saving != 0) map[x][y] = O;
			if (mining == 2) mining = 0;
			if (mining == 1) {
				if ((map[x+1][y] == X || vin) && !outside(x+1, y))
					makeminer(x+1, y, vin);
				else if ((map[x][y+1] == X || vin) && !outside(x, y+1))
					makeminer(x, y+1, vin);
			} else if ((map[x][y+1] == X || vin) && !outside(x, y+1))
				makeminer(x, y+1, vin);
			else if ((map[x+1][y] == X || vin) && !outside(x+1, y))
					makeminer(x+1, y, vin);
		}
	}
	
	bool mark[maxr][maxc] = {{false}};
	
	virtual bool cango(int x, int y) {
		if (x == fin_x && y == fin_y)
			return true;
		else if (!inside(x, y))
			return false;
		else if (mark[x][y])
			return false;
		else {
			mark[x][y] = true;
			bool ret = cango(x+1,y) || cango(x,y+1) || cango(x-1,y) || cango(x,y-1);
			mark[x][y] = false;
			return ret; 
		};
	}
	
	virtual void miner() {
		srand((unsigned long)time(NULL));
		
		vector<Point> miners;
		int miner_c = 50;
		
		for (int i=0; i<miner_c; ++i) {
			miners.push_back(spawnminer());
		}
		
		for (int i=0; i<miner_c; ++i) {
			makeminer(miners[i].x, miners[i].y, false);
		}
		
		makeminer(1, 1, true);
	}

};

class MapManager : public AbstractMapManager {
	
public:
		
	MapManager() {
		initialization();
		miner();
		repaint();
	}
	
};

class DarkMap : public MapManager {
	
public:
	
	int xrange, yrange;
	
	DarkMap() {
		MapManager();
		xrange = 2;
		yrange = 4;
		repaint();
	}
	
	virtual int score_max() {
		return 20;
	}
	
	virtual bool inrange(int x, int y) {
		return x >= now_x-xrange && x <= now_x+xrange && y >= now_y-yrange && y <= now_y+yrange;
	}
	
	virtual void repaint() {
		
		system("cls");
		for (int i=0; i<maxr; ++i) {
			for (int j=0; j<maxc; ++j) {
				if (inrange(i, j)) {
					if (i == now_x && j == now_y)
						printf("%c", M);
					else if (i == fin_x && j == fin_y)
						printf("%c", N);
					else printf("%c", map[i][j]);
				} else printf("%c", '/');
			}
			printf("\n");
		}
		
	}
	
};

class HardnessMap : public MapManager {
	
public:
	
	virtual bool move(int difx, int dify) {
		
		int x = now_x + difx, y = now_y + dify;
		if (inside(x, y)) {
			map[now_x][now_y] = X;
			now_x = x;
			now_y = y;
		}
		repaint();
		return fin(x, y);
	}
	
	virtual int score_max() {
		return 15;
	}
	
};

/* bool check[35][70]={false};
static const int gowhere[8][2] = 
{
	{0,-1},
	{0,1},
	{-1,0},
	{1,0},
	{-1,-1},
	{1,1},
	{1,-1},
	{-1,1},	
};

bool _cango(int nowr,int nowc){
	if(!rightxy(nowr,nowc) || check[nowr][nowc]){
		return false;
	}
	if(fin(nowr,nowc)){
		return true;
	}
	check[nowr][nowc] = true;
	bool ret = false;
	for(int i = 0; i < 8 ;++i){
		ret = _cango(gowhere[i][0]+nowr,gowhere[i][1]+nowc) || ret;
	}
	check[nowr][nowc] = false;
}

bool cango(){
	return _cango(1,1);
} */

int main() {
	
	AbstractMapManager* map = new AbstractMapManager();
	system("color 2A");
	
	back: system("cls");
	printf("What mode do you want to play? (Enter a number)\n");
	printf("1. Blindness mode\n");
	printf("2. Hardness mode\n");
	printf("3. Normal mode\n");
	
	int selection = getch() - '0';
	int score = 0;
	double timerec = 9999.99;
	
	replay:
	double past = clock();
	
	while (true) {
	delete map;
	
	switch(selection) {
		case 1:
			map = new DarkMap();
			break;
		case 2:
			map = new HardnessMap();
			break;
		case 3:
			map = new MapManager();
			break;
		case 'q'-'0': case 'Q'-'0':
			exit(0);
			break;
		default:
			printf("It\'s a wrong number.\n");
			system("pause");
			goto back;
	}
	
	
	while(true) {
		
		printf("Your score: %d\n",score);
		
		int difx = 0, dify = 0;
		int nx;
		char x;
		x = getch();
		
		switch(x) {
			case 'w': case 'W':
				difx = -1;
				break;
			case 'a': case 'A':
				dify = -1;
				break;
			case 's': case 'S':
				difx = 1;
				break;
			case 'd': case 'D':
				dify = 1;
				break;
			case 'q': case 'Q':
				system("cls");
				printf("Your score: %d\n", score);
				printf("Do you want to exit? (Y/N)\n");
				nx = getch();
				if (nx == 'Y' || nx == 'y') {
					exit(0);
				}
				else break;
			case 'r': case 'R':
				system("cls");
				printf("Your score: %d\n", score);
				printf("Do you want to play with a new game? (Y/N)\n");
				nx = getch();
				if (nx == 'Y' || nx == 'y') {
					goto replay;
				}
				else break;
			case 'e': case 'E':
				system("cls");
				printf("Your score: %d\n", score);
				printf("Do you want to play with other modes? (Y/N)\n");
				nx = getch();
				if (nx == 'Y' || nx == 'y') {
					goto back;
				}
				else break;
			default:
				map->repaint(); 
				printf("Error");
				continue;
		}
		
		if (map->move(difx, dify)) {
			printf("You win!\n");
			system("pause");
			break;
		}
		
	}
	
	system("cls");
	
	double now = clock();
	now = (now-past)/1000.0;
	
	if (now < timerec) {
		printf("***** New time recording! *****\n");
		timerec = now;
	}
	printf("Time recording: %.2lf seconds\n", timerec);
	printf("Time you spent: %.2lf seconds\n\n", now);
	
	score += map->score_max();
	printf("Your score: %d\n", score);
	printf("Do you want to continue? (Y/N)\n");
	int n = getch();
	if (n == 'Y' || n == 'y') continue;
	else break;
	
	}
	
	delete map;
	
	return 0;	
}